package org.yuangu.privacyfinance.domain;

import android.content.Context;

import com.privacy.accountmgr.R;

import net.sqlcipher.database.SupportFactory;

import org.yuangu.privacyfinance.activities.helper.CommunicantAsyncTask;
import org.yuangu.privacyfinance.activities.helper.TaskListener;
import org.yuangu.privacyfinance.domain.access.AccountDao;
import org.yuangu.privacyfinance.domain.access.CategoryDao;
import org.yuangu.privacyfinance.domain.access.RepeatingTransactionDao;
import org.yuangu.privacyfinance.domain.access.TransactionDao;
import org.yuangu.privacyfinance.domain.convert.LocalDateConverter;
import org.yuangu.privacyfinance.domain.model.Account;
import org.yuangu.privacyfinance.domain.model.Category;
import org.yuangu.privacyfinance.domain.model.RepeatingTransaction;
import org.yuangu.privacyfinance.domain.model.Transaction;
import org.yuangu.privacyfinance.helpers.KeyStoreHelper;
import org.yuangu.privacyfinance.helpers.KeyStoreHelperException;

import java.io.File;

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;

/**
 * 主数据库类。兼处理数据库的初始化。
 */
@Database(
        entities = {Account.class, Category.class, Transaction.class, RepeatingTransaction.class},
        exportSchema = false,
        version = 1
)
@TypeConverters({LocalDateConverter.class})
public abstract class FinanceDatabase extends RoomDatabase {
    private static final String DB_NAME = "encryptedDB";
    public static final String KEY_ALIAS = "financeDatabaseKey";
    private static InitDatabaseTask initTask;
    private static FinanceDatabase instance;

    public abstract TransactionDao transactionDao();

    public abstract CategoryDao categoryDao();

    public abstract AccountDao accountDao();

    public abstract RepeatingTransactionDao repeatingTransactionDao();

    public static FinanceDatabase getInstance() {
        return instance;
    }

    public static CommunicantAsyncTask<?, FinanceDatabase> connect(Context context, TaskListener listener) {
        if (initTask == null) {
            initTask = new InitDatabaseTask(context, DB_NAME);
            if (listener != null) initTask.addListener(listener);
            initTask.execute();
        }
        return initTask;
    }


    private static class InitDatabaseTask extends CommunicantAsyncTask<Void, FinanceDatabase> {
        private Context context;
        private String dbName;

        public InitDatabaseTask(Context context, String dbName) {
            this.context = context;
            this.dbName = dbName;
        }

        private void deleteDatabaseFile() {
            File databaseDir = new File(context.getApplicationInfo().dataDir + "/databases");
            File[] files = databaseDir.listFiles();
            if (files != null) {
                for (File f : files) {
                    if (!f.isDirectory() && f.getName().startsWith(DB_NAME)) {
                        f.delete();
                    }
                }
            }
        }

        private boolean dbFileExists() {
            File databaseFile = new File(context.getApplicationInfo().dataDir + "/databases/" + DB_NAME);
            return databaseFile.exists() && databaseFile.isFile();
        }

        @Override
        protected FinanceDatabase doInBackground(Void... voids) {
            try {

                publishProgress(0.0);
                publishOperation(context.getResources()
                        .getString(R.string.activity_startup_init_key_store_msg));
                KeyStoreHelper keystore = KeyStoreHelper.getInstance(KEY_ALIAS);

                if (!keystore.keyExists()) {
                    deleteDatabaseFile();
                    publishOperation(context.getResources()
                            .getString(R.string.activity_startup_open_database_msg));
                }

                byte[] key = keystore.getKeyBytes(context);
                if (key == null) {
                    throw new RuntimeException("Key could not be retrieved!");
                }

                publishProgress(.6);
                if (dbFileExists()) {
                } else {
                    publishOperation(context.getResources()
                            .getString(R.string.activity_startup_create_and_open_database_msg));
                }
                FinanceDatabase.instance =
                        Room.databaseBuilder(context, FinanceDatabase.class, dbName)
                        .openHelperFactory(new SupportFactory(key))
                        .build();

                if (FinanceDatabase.instance.accountDao().count() == 0) {
                    Account defaultAccount = new Account(context.getResources()
                            .getString(R.string.activity_startup_default_account_name));
                    defaultAccount.setId(0L);
                    FinanceDatabase.instance.accountDao().insert(defaultAccount);
                }
                publishProgress(.8);
                return FinanceDatabase.instance;
            } catch (KeyStoreHelperException ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}
